package net.mgsx.product.t2m.modisco.impact.analyser;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.mgsx.product.t2m.modisco.impact.AnalysisInformation;
import net.mgsx.product.t2m.modisco.tools.ModiscoUtils;

import org.eclipse.gmt.modisco.java.ASTNode;
import org.eclipse.gmt.modisco.java.AbstractTypeDeclaration;
import org.eclipse.gmt.modisco.java.CompilationUnit;
import org.eclipse.gmt.modisco.java.Model;
import org.eclipse.gmt.modisco.java.TypeAccess;

/**
 * Impact analyser at file level.
 * Rules :<br>
 * - a modified node implies the containing file (compilation unit) to be modified<br>
 * - a modified file implies impact for all files having any reference to elements contained in the modified file<br>
 * 
 * @author mgsx
 */
public class FileImpactAnalyser extends AbstractImpactAnalyser {

	@Override
	public Map<ASTNode, AnalysisInformation> analyse(List<ASTNode> modifiedNodes) {
		
		Map<ASTNode, AnalysisInformation> results = new HashMap<ASTNode, AnalysisInformation>();
		
		// 0) find models
		List<Model> models = new ArrayList<Model>();
		for(ASTNode node : modifiedNodes){
			Model model = ModiscoUtils.getOwner(node, Model.class);
			if(!models.contains(model)){
				models.add(model);
			}
		}
		if(models.size() == 0){
			ModiscoUtils.getLogger().logWarning("no models found");
		}
		
		// 1) compute global informations
		for(Model model : models){
			for(CompilationUnit cu : model.getCompilationUnits()){
				results.put(cu, createAnalysisInformation(cu));
			}
		}
		if(results.size() == 0){
			ModiscoUtils.getLogger().logWarning("no compilation units found");
		}
		
		// 2) make modified file list and find impacts.
		for(ASTNode modifiedNode : modifiedNodes)
		{
			CompilationUnit modifiedUnit = modifiedNode.getOriginalCompilationUnit();
			if(modifiedUnit != null){
				findImpactsRecursive(modifiedUnit, results);
			}else{
				ModiscoUtils.getLogger().logWarning("no compilation units for element " + modifiedNode.eClass().getName());
			}
		}
		
		return results;
	}
	
	private void findImpactsRecursive(CompilationUnit unit, Map<ASTNode, AnalysisInformation> results){
		AnalysisInformation info = results.get(unit);
		for(ASTNode cu : results.keySet()){
			if(cu instanceof CompilationUnit && ((CompilationUnit)cu).getOriginalFilePath().equals(unit.getOriginalFilePath())){
				info = results.get(cu);
			}
		}
		if(!info.impacted)
		{
			info.impacted = true;
			for(AbstractTypeDeclaration type : unit.getTypes()){
				for(TypeAccess access : type.getUsagesInTypeAccess()){
					AbstractTypeDeclaration typeOwner = ModiscoUtils.getOwner(access, AbstractTypeDeclaration.class);
					if(typeOwner != null){
						CompilationUnit impactedUnit = typeOwner.getOriginalCompilationUnit();
						if(impactedUnit != null){
							findImpactsRecursive(impactedUnit, results);
						}else{
							ModiscoUtils.getLogger().logWarning("no compilation units for element " + typeOwner.getName());
						}
					}else{
						ModiscoUtils.getLogger().logWarning("no owner for type access to " + type.getName());
						
					}
				}
			}
		}else{
			ModiscoUtils.getLogger().logWarning("no info for compilation unit " + unit.getOriginalFilePath());
		}
	}
	
	private AnalysisInformation createAnalysisInformation(CompilationUnit node){
		final AnalysisInformation info = new AnalysisInformation();
		info.nbInstructions = 0;
		for(AbstractTypeDeclaration type : node.getTypes()){
			info.nbInstructions += ModiscoUtils.getElements(type, ASTNode.class).size();
		}
		info.impacted = false;
		return info;
	}

}
